package xfuzzy.xfhl.model;

import java.util.Observable;
import java.util.Observer;
import xfuzzy.lang.Specification;
import xfuzzy.xfsl.model.XfslThread;

/**
 * Clase que encapsula el ajuste de una specificacion
 */

public class XfhlData extends Observable implements Observer {

	public static final int NEW_SPEC = 1;
	public static final int DEC_EPOCH = 2;
	public static final int FINAL_SPEC = 3;

	/**
	 * Identificador
	 */
	private int idData;

	/**
	 * Specification que tiene el XfhlData
	 */
	private Specification spec;

	/**
	 * Epocas totales
	 */
	private int epochIni;

	/**
	 * Epocas actuales
	 */
	private int epoch;

	/**
	 * Para saber cuando hemos terminado de entrenar la especificacion
	 */
	private boolean finalSpec;

	/**
	 * El error final obtenido
	 */
	private double finalError;

	/**
	 * Atributo para saber el tiempo cuando comienza una estructura
	 */
	private long timeIni;

	/**
	 * Atributo para saber el tiempo cuando termina una estructura
	 */
	private long timeFin;

	/**
	 * Atributo para los que los observers sepan el cambio que ha sufrido
	 */
	private int CHANGE;

	/**
	 * Constructor
	 * */
	public XfhlData(int idData, int epoch) {
		this.idData = idData;
		this.spec = null;
		this.epoch = epoch;
		this.setFinalSpec(false);
		this.epochIni = epoch;
	}

	/**
	 * Reinicia las epocas
	 */
	private void resetEpoch() {
		this.epoch = epochIni;
	}

	/**
	 * Actualizamos la specificacion
	 * 
	 * @param spec
	 *            nueva specification
	 */
	public void setSpec(Specification spec) {
		this.timeIni = System.currentTimeMillis();
		this.setFinalSpec(false);
		this.spec = spec;
		this.CHANGE = XfhlData.NEW_SPEC;
		resetEpoch();
		setChanged();
		notifyObservers();
		clearChanged();
	}

	/**
	 * Decrementamos la epoca en uno
	 */
	public void decreEpoch() {
		this.CHANGE = XfhlData.DEC_EPOCH;
		this.epoch--;
		setChanged();
		notifyObservers();
		clearChanged();
	}

	/**
	 * Notifica a todos los observadores que ha terminado y actualiza su error
	 * 
	 * @param finalError
	 */
	public void setFinalErrorNotify(double finalError) {
		this.timeFin = System.currentTimeMillis();
		this.CHANGE = XfhlData.FINAL_SPEC;
		this.setFinalSpec(true);
		this.finalError = finalError;
		setChanged();
		notifyObservers();
		clearChanged();
	}

	/**
	 * GETTER AND SETTERS
	 * 
	 * @return
	 */

	public Specification getSpec() {
		return this.spec;
	}

	public int getChange() {
		return this.CHANGE;
	}

	public int getEpochIni() {
		return this.epochIni;
	}

	public int getEpoch() {
		return this.epoch;
	}

	public double getFinalError() {
		return this.finalError;
	}

	public int getIdData() {
		return this.idData;
	}

	public long getTime() {
		return this.timeFin - this.timeIni;
	}

	/**
	 * Metodo para implementar la interfaz Observer, lo utilizamos para
	 * comunicarnos con su objeto observable XfslThread
	 */
	public void update(Observable o, Object arg) {
		// Comprobamos que sea un Thread la que ha sido modificada
		if (o instanceof XfslThread) {
			int currentState = ((XfslThread) o).getChange();
			switch (currentState) {
			// Si hemos terminado actualizamos el error del Data
				case XfslThread.FINAL_SPEC:
					setFinalErrorNotify(((XfslThread) o).getStatus().trn.rmse);
					// System.out.println("XfhlData "+this.idData+": Final de especificaci�n "+((XfslThread)o).getStatus().trn.rmse);
					break;
				// Decrementamos una iteracion
				case XfslThread.DEC_EPOCH:
					decreEpoch();
					// System.out.println("XfhlData "+this.idData+": Iteraci�n menos");
				default:
					break;
			}
		}
	}

	/**
	 * @param finalSpec
	 *            the finalSpec to set
	 */
	public void setFinalSpec(boolean finalSpec) {
		this.finalSpec = finalSpec;
	}

	/**
	 * @return the finalSpec
	 */
	public boolean isFinalSpec() {
		return finalSpec;
	}
}
